#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "graph.h"
#include "statistics.h"

void stat_PR_init(Stat_PR_t * p_stat, Graph_t * p_graph)
{
  p_stat->stat_p_max_var_vals = (int *)calloc(p_graph->graph_num_vars,
                                         sizeof(*p_stat->stat_p_max_var_vals));
  p_stat->stat_max_weight = -HUGE_VAL;
  p_stat->stat_num_max_updates = 0;
  p_stat->stat_num_updates = 0;
}

void stat_PR_update(Stat_PR_t * p_stat, Graph_t * p_graph, int * var_vals)
{
  /* check if this is different from the current max */
  if (!p_stat->stat_num_max_updates || memcmp(p_stat->stat_p_max_var_vals,
                                              var_vals, p_graph->graph_num_vars
                                              * sizeof(*var_vals)))
  {
    double weight;
    
    weight = graph_joint_factor(p_graph, var_vals);

    if (weight > p_stat->stat_max_weight)
    {
      p_stat->stat_max_weight = weight;
      p_stat->stat_num_max_updates = 1;
      p_stat->stat_num_updates = 1;
      memcpy(p_stat->stat_p_max_var_vals, var_vals, p_graph->graph_num_vars
             * sizeof(*var_vals));
    }
    else
    {
      p_stat->stat_num_updates += 1;
    }
  }
  else
  {
    p_stat->stat_num_max_updates += 1;
    p_stat->stat_num_updates += 1;
  }
}

void stat_PR_write(Stat_PR_t * p_stat, Graph_t * p_graph, FILE * fp)
{
  printf("max log-weight %g, max updates %d, num updates %d\n",
         p_stat->stat_max_weight, p_stat->stat_num_max_updates,
         p_stat->stat_num_updates);
  
  fprintf(fp, "%g\n", (p_stat->stat_max_weight + log(p_stat->stat_num_updates)
                       - log(p_stat->stat_num_max_updates))
          / log(10.0));

  free(p_stat->stat_p_max_var_vals);
}

void stat_MPE_init(Stat_MPE_t * p_stat, Graph_t * p_graph)
{
  p_stat->stat_p_var_vals = (int *)calloc(p_graph->graph_num_vars,
                                          sizeof(*p_stat->stat_p_var_vals));
  p_stat->stat_weight = -HUGE_VAL;
}

void stat_MPE_update(Stat_MPE_t * p_stat, Graph_t * p_graph, int * var_vals)
{
  double weight;
  
  weight = graph_joint_factor(p_graph, var_vals);
  
  if (weight > p_stat->stat_weight)
  {
    int v;
    
    for (v=0; v<p_graph->graph_num_vars; v++)
      p_stat->stat_p_var_vals[v] = var_vals[v];
    
    p_stat->stat_weight = weight;
  }
}

void stat_MPE_write(Stat_MPE_t * p_stat, Graph_t * p_graph, FILE * fp)
{
  int v;

  printf("max log-weight %g\n", p_stat->stat_weight);
  
  fprintf(fp, "%d", p_graph->graph_num_vars);
  
  for (v=0; v<p_graph->graph_num_vars; v++)
  {
    fprintf(fp, " %d", p_stat->stat_p_var_vals[v]);
  }
  
  fprintf(fp, "\n");

  free(p_stat->stat_p_var_vals);
}

void stat_MAR_init(Stat_MAR_t * p_stat, Graph_t * p_graph)
{
  int v;
  
  p_stat->stat_pp_cnt_var_vals = (int **) malloc(p_graph->graph_num_vars
                                     * sizeof(*p_stat->stat_pp_cnt_var_vals));
  
  for (v=0; v<p_graph->graph_num_vars; v++)
  {
    Variable_t * p_var;
    
    p_var = p_graph->graph_p_vars + v;

    p_stat->stat_pp_cnt_var_vals[v] = (int *) calloc(p_var->var_card,
                                    sizeof(*p_stat->stat_pp_cnt_var_vals[v]));
  }

  p_stat->stat_num_updates = 0;
}


void stat_MAR_update(Stat_MAR_t * p_stat, Graph_t * p_graph, int * var_vals)
{
  int v;
  
  for (v=0; v<p_graph->graph_num_vars; v++)
  {
    p_stat->stat_pp_cnt_var_vals[v][var_vals[v]] ++;
  }
  
  p_stat->stat_num_updates ++;
}

void stat_MAR_write(Stat_MAR_t * p_stat, Graph_t * p_graph, FILE * fp)
{
  int v;
  
  fprintf(fp, "%d", p_graph->graph_num_vars);
  
  for (v=0; v<p_graph->graph_num_vars; v++)
  {
    Variable_t * p_var;
    int * p_cnt_var_vals;
    int c;
    
    p_var = p_graph->graph_p_vars + v;
    p_cnt_var_vals = p_stat->stat_pp_cnt_var_vals[v];
    
    fprintf(fp, " %d", p_var->var_card);

    for (c=0; c<p_var->var_card; c++)
    {
      fprintf(fp, " %g", ((double) p_cnt_var_vals[c]) 
              / ((double) p_stat->stat_num_updates));
    }
  }

  fprintf(fp, "\n");

  /* free the memory for the statistics */
  for (v=0; v<p_graph->graph_num_vars; v++)
  {
    Variable_t * p_var;
    
    p_var = p_graph->graph_p_vars + v;

    free(p_stat->stat_pp_cnt_var_vals[v]);
  }

  free(p_stat->stat_pp_cnt_var_vals);
}


void stat_BEL_init(Stat_BEL_t * p_stat, Graph_t * p_graph)
{
  int f;
  
  p_stat->stat_pp_cnt_fac_vals = (int **) malloc(p_graph->graph_num_facs
                                     * sizeof(*p_stat->stat_pp_cnt_fac_vals));
  
  for (f=0; f<p_graph->graph_num_facs; f++)
  {
    Factor_t * p_fac;
    
    p_fac = p_graph->graph_p_facs + f;

    p_stat->stat_pp_cnt_fac_vals[f] = (int *) calloc(p_fac->fac_num_entries,
                                    sizeof(*p_stat->stat_pp_cnt_fac_vals[f]));
  }

  p_stat->stat_num_updates = 0;
}


void stat_BEL_update(Stat_BEL_t * p_stat, Graph_t * p_graph, int * var_vals)
{
  int f;
  
  for (f=0; f<p_graph->graph_num_facs; f++)
  {
    Factor_t * p_fac;
    int index;
    int v;
    
    p_fac = p_graph->graph_p_facs + f;
    
    index = 0;

    for (v=0; v<p_fac->fac_num_vars; v++)
    {
      Variable_t * p_var;
    
      p_var = p_fac->fac_p_vars[v];
    
      index = index * p_var->var_card + var_vals[p_var->var_id];
    }
    
    p_stat->stat_pp_cnt_fac_vals[f][index] ++;
  }
  
  p_stat->stat_num_updates ++;
}

void stat_BEL_write(Stat_BEL_t * p_stat, Graph_t * p_graph, FILE * fp)
{
  int f;
  
  fprintf(fp, "%d", p_graph->graph_num_facs);
  
  for (f=0; f<p_graph->graph_num_facs; f++)
  {
    Factor_t * p_fac;
    int * p_cnt_fac_vals;
    int c;
    
    p_fac = p_graph->graph_p_facs + f;
    p_cnt_fac_vals = p_stat->stat_pp_cnt_fac_vals[f];
    
    fprintf(fp, " %d", p_fac->fac_num_entries);

    for (c=0; c<p_fac->fac_num_entries; c++)
    {
      fprintf(fp, " %g", ((double) p_cnt_fac_vals[c]) 
              / ((double) p_stat->stat_num_updates));
    }
  }

  fprintf(fp, "\n");

  /* free the memory for the statistics */
  for (f=0; f<p_graph->graph_num_facs; f++)
  {
    Factor_t * p_fac;
    
    p_fac = p_graph->graph_p_facs + f;

    free(p_stat->stat_pp_cnt_fac_vals[f]);
  }
  
  free(p_stat->stat_pp_cnt_fac_vals);
}

